package top.lingkang.mm.orm;

import cn.hutool.core.lang.Assert;
import top.lingkang.mm.error.MagicException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @author lingkang
 * @create by 2024/3/12 16:13
 */
public class Query extends BaseQuery implements BaseQueryInterface<Query>{

    /**
     * 查询条件，自定义操作符号;<br/>
     * 例如 sort_number > 1 ； sort_number = 2 ; sort_number > 3 ; sort_number >= 1; <br/>
     * 例如 sort_number is null ； sort_number = 2 ; sort_number > 3 ; sort_number >= 1; <br/>
     * <pre>
     * {@code
     * // 例示
     * UserEntity entity = userMapper.selectByQueryOne(
     *                 new Query().symbol("id", "=", 1)
     *         );
     * log.info("selectByQueryOne: {}", entity);
     * }
     * </pre>
     *
     * @param column sort_number
     * @param symbol >
     * @param value  12
     * @return sort_number > 12
     * @since 1.1.0
     */
    public Query symbol(String column, String symbol, Object value) {
        list.add(new ConditionValue(Condition.symbol, column, value).setSymbol(symbol));
        return this;
    }

    public Query eq(String column, Object value) {
        list.add(new ConditionValue(Condition.eq, column, value));
        return this;
    }

    public Query ne(String column, Object value) {
        list.add(new ConditionValue(Condition.ne, column, value));
        return this;
    }

    public Query gt(String column, Object value) {
        list.add(new ConditionValue(Condition.gt, column, value));
        return this;
    }

    public Query ge(String column, Object value) {
        list.add(new ConditionValue(Condition.ge, column, value));
        return this;
    }

    public Query lt(String column, Object value) {
        list.add(new ConditionValue(Condition.lt, column, value));
        return this;
    }

    public Query le(String column, Object value) {
        list.add(new ConditionValue(Condition.le, column, value));
        return this;
    }

    public Query like(String column, Object value) {
        list.add(new ConditionValue(Condition.like, column, value));
        return this;
    }

    public Query notLike(String column, Object value) {
        list.add(new ConditionValue(Condition.notLike, column, value));
        return this;
    }

    public Query likeLeft(String column, Object value) {
        list.add(new ConditionValue(Condition.likeLeft, column, value));
        return this;
    }

    public Query likeRight(String column, Object value) {
        list.add(new ConditionValue(Condition.likeRight, column, value));
        return this;
    }

    public Query notLikeLeft(String column, Object value) {
        list.add(new ConditionValue(Condition.notLikeLeft, column, value));
        return this;
    }

    public Query notLikeRight(String column, Object value) {
        list.add(new ConditionValue(Condition.notLikeRight, column, value));
        return this;
    }


    /**
     * column is null
     */
    public Query isNull(String column) {
        list.add(new ConditionValue(Condition.isNull, column, null));
        return this;
    }

    /**
     * column is not null
     */
    public Query isNotNull(String column) {
        list.add(new ConditionValue(Condition.isNotNull, column, null));
        return this;
    }

    /**
     * <pre>
     * {@code
     *     new Query().in("id", Arrays.asList(1, 19951219, 3))
     * }
     * </pre>
     */
    public Query in(String column, Collection value) {
        list.add(new ConditionValue(Condition.in, column, value));
        return this;
    }

    /**
     * <pre>
     * {@code
     *     new Query().notIn("id", Arrays.asList(1, 19951219, 3))
     * }
     * </pre>
     */
    public Query notIn(String column, Collection value) {
        list.add(new ConditionValue(Condition.notIn, column, value));
        return this;
    }

    public Query orderByAsc(String... ascColumn) {
        Assert.notNull(ascColumn, "排序的列不能为空");
        if (orderBy == null)
            orderBy = new ArrayList<>();
        orderBy.add(new ConditionOrderBy(Condition.orderByAsc, ascColumn));
        return this;
    }

    public Query orderByDesc(String... descColumn) {
        Assert.notNull(descColumn, "排序的列不能为空");
        if (orderBy == null)
            orderBy = new ArrayList<>();
        orderBy.add(new ConditionOrderBy(Condition.orderByDesc, descColumn));
        return this;
    }

    public Query or() {
        list.add(new ConditionValue(Condition.or));
        return this;
    }

    /**
     * 追加sql，注意顺序，查询中存在 orderByAsc 或者 orderByAsc 时，<br>
     * orderByAsc 或者 orderByAsc 将会生成到sql最后面<br>
     * 例如 new Query().orderByAsc("id").sql(" name=#{name}") 报错<br>
     * 将会输出错误sql：select * from my_table order by id asc name=#{name}
     *
     * @param sql 要追加的sql，例如 limit 1，输出sql：select id from t_user where 1=1 limit 1
     * @return 追加sql
     */
    public Query sql(String sql) {
        list.add(new ConditionValue(Condition.sql, sql, null));
        return this;
    }

    /**
     * 追加sql，注意顺序，查询中存在 orderByAsc 或者 orderByAsc 时，<br>
     * orderByAsc 或者 orderByAsc 将会生成到sql最后面<br>
     * 例如 new Query().orderByAsc("id").sql(" name=#{name}") 报错<br>
     * 将会输出错误sql：select * from my_table order by id asc name=#{name}
     *
     * @param sql   id = #{id} and type= #{type}
     * @param param new MapParam().add("id","1").add("type","user")
     * @return 查询对象
     */
    public Query sql(String sql, Map<String, Object> param) {
        list.add(new ConditionValue(Condition.sql, sql, param));
        return this;
    }

    protected void addQueryParam(StringBuilder sql, String p) {
        sql.append("#{").append(BaseMapperSql.param_q).append(".param.").append(p).append("}");
    }


    // get 、 set

    // mybatis的底层将会调用获取SQL
    public String getSql() {
        if (list.isEmpty() && orderBy == null)
            return "";
        return "where 1=1" + buildSql();
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    public HashMap<String, Object> getParam() {
        return param;
    }

    public void setParam(HashMap<String, Object> param) {
        this.param = param;
    }
}
